
#ifndef TYPE_H
#define TYPE_H

#include <assert.h>
#include <vector>
#include <iostream>

namespace mjc {

	class Object;

	class Type
	{
	public:
		enum Kind
		{
			K_NONE,
			K_INT,
			K_CHAR,
			K_ARRAY,
			K_CLASS
		};

		static class ArrayData
		{
			Type* type;//array data type
		public:
			ArrayData();
			ArrayData(const ArrayData& ad);
			ArrayData(const Type& type);
			~ArrayData();
			ArrayData& operator = (const ArrayData& ad);
			Type& GetType() const { return *type; }
			void SetType(const Type& type) { *this->type = type; }
		};

		static class ClassData
		{
			//TODO: std::map<std::wstring, Object*>
			std::vector<Object*> fields;//actual fields
		public:
			ClassData();
			ClassData(const ClassData& cd);
			~ClassData();
			ClassData& operator = (const ClassData& cd);
			void AddField(const Object& field);
			Object& GetField(unsigned int index) const { return *fields[index]; }
			Object* FindField(const std::wstring& name);
			unsigned int FieldCount() { return fields.size(); }
		};

	private:
		Kind kind;
		ArrayData* arrayData;
		ClassData* classData;

		Type(){}

	public:
		Type(const Kind& kind);
		Type(const ArrayData& ad);
		Type(const ClassData& cd);
		Type(const Type& other);
		~Type();
		Type& operator = (const Type& other);

		bool operator == (const Type& other) const;
		bool operator != (const Type& other) const;
		bool IsReferenceType() const;
		bool Compatible(const Type& other) const;
		bool Assignable(const Type& other) const;

		Kind GetKind() const { return kind; }
		ArrayData& GetArrayData();
		ArrayData GetArrayData() const;
		void SetArrayData(const ArrayData& ad);
		ClassData& GetClassData();
		ClassData GetClassData() const;
		void SetClassData(const ClassData& cd);

		friend std::wostream& operator << (std::wostream& os, const Type& type);
	};
}

#endif